package at.donalfonso.koch;

import java.io.Serializable;
import java.util.Random;

import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageProducer;
import javax.jms.ObjectMessage;
import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.Topic;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import at.donalfonso.model.Bestellung;
import at.donalfonso.model.LieferBestellung;
import at.donalfonso.model.Pizza;
import at.donalfonso.model.Pizza.PizzaStatusEnum;
import at.donalfonso.utility.JMSUtility;

public class KochJMS extends Thread {

	private Logger logger = LoggerFactory.getLogger(this.getClass());
	private JMSUtility jmsUtility = null;

	private MessageConsumer consumer;
	public boolean waitTime = true;
	
	public String name;
	
	/* Ein Kellner ist genau einer Filiale zugeordnet (1 oder 2) */
	public int filiale;

	public static void main(String[] args){

		KochJMS koch;
		
		if (args.length < 2) {
			System.out.println("Usage: java Koch <ID> <Filiale> [<noWaitTime>]\nWenn ein dritter Parameter mitgegeben wird, wird die Wartezeit deaktiviert.");
			Random random = new Random();
			koch = new KochJMS("Ko" + random.nextInt(100), 1);
		} else {
			koch = new KochJMS(args[0], Integer.parseInt(args[1]));
		}
		
		if(args.length == 3){
			// Wartezeit deaktivieren.
			koch.waitTime = false;
		}
		

		koch.run();

	}
	
	public void run(){
		try{
			MessageConsumer consumer;
			Queue queue;
			Message message;
			
			/**
			 * Der Koch ist dafür verantwortlich die Pizzen der noch unbearbeiteten Bestellungen in
			 * die Queue kuecheInput zu stellen.
			 */		
			while(true){
				
				// Versuche Pizzen zu lesen			
				queue = getJMSUtility().getQueueKuecheInput();
				consumer = getJMSUtility().getSession().createConsumer(queue);
				logger.debug("nach createConsumer");
				
				//System.out.println("Versuche Pizza zu lesen ...");
				/* Pizzen aus der Queue Kueche_input empfangen */
				message = consumer.receive(1000);
				consumer.close();
								
				if (message != null){
					//System.out.println("Pizza gefunden: "+message);
					
				
					kochen(message);
				}else{
					
					queue = getJMSUtility().getQueueBestellungAufgenommen();
					consumer = getJMSUtility().getSession().createConsumer(queue);
					logger.debug("nach createConsumer");
				
					//System.out.println("Versuche Bestellung zu lesen ...");
					/* Pizzen aus der Queue Kueche_input empfangen */
					message = consumer.receive(1000);
					consumer.close();
					
					if (message != null){
						System.out.println("Bestellung gelesen.");
						
						Bestellung bestellung = (Bestellung)((ObjectMessage) message).getObject();
						bestellungBearbeiten(bestellung);
					}
				}
				
				// Wenn keine Pizzen gefunden wurden: Bestellung einlesen
			}
		}catch(JMSException e){
			
		}
		
	}

	/**
	 * Pizzen aus Bestellung lesen.
	 * 
	 * Bestellungen werden aus der Queue BestellungAufgenommen gelesen und 
	 * in die Queue BestellungOffen geschrieben. Dabei werden die Pizzen ausgelesen
	 * und in die Queue kueche_input gestellt.
	 */
	public void bestellungBearbeiten(Bestellung bestellung){
		try{
			
			Session session1;

System.out.println("bestellungBearbeiten: "+bestellung.getBestellungId());			
			
			// Die gelesene Message kann nicht direkt wiederverwendet werden. (MessageNotWritableException)
			Queue queue = getJMSUtility().getQueueBestellungOffen();
			session1 = getJMSUtility().getConnection().createSession(false, Session.AUTO_ACKNOWLEDGE);
			MessageProducer producer = session1.createProducer(queue);
			Message message = session1.createObjectMessage(bestellung);
			
			((ObjectMessage) message).setStringProperty("ID", bestellung.getBestellungId());
			
			producer.send(message);
			producer.close();
			System.out.println("Bestellung nach Bestellung offen geschrieben.");
			
			if(bestellung instanceof LieferBestellung){
			
				// Für den Loadbalancer wird die Lieferbestellung auch in das Topic Lieferung geschrieben.
				producer = session1.createProducer(getJMSUtility().getTopicLieferung());
				message = session1.createObjectMessage(bestellung);
				message.setStringProperty("STATUS", "IN_ARBEIT");
				producer.send(message);
				producer.close();
				
				System.out.println("Bestellung in Topic "+getJMSUtility().getTopicLieferung()+" geschrieben.");
				
			}
			
			// Pizzen werden ab jetzt vom Koch aufgenommen
			queue = getJMSUtility().getQueueKuecheInput();
			producer = session1.createProducer(queue);
			//System.out.println("Pizzen in queue");
			for(Pizza p: bestellung.getPizzen()) {
				
				p.setBestellnummer(bestellung.getBestellungId());
				
				ObjectMessage omp = session1.createObjectMessage(p);
				omp.setStringProperty("ID", p.getId());
				producer.send(omp);
				
				System.out.println("Pizza in kueche_input geschrieben: "+p);
			}
			
			producer.close();
			session1.close();

		}catch(JMSException e){
			System.out.println("catch "+e);
		}
	}
	
	/**
	 * Der Koch wartet auf Aufträge in der Queue Kueche_input.
	 * @param name
	 */
	public KochJMS(String name, int filiale) {
		
		this.name = name;
		this.filiale = filiale;
		
		try {

			getJMSUtility().getConnection().start();
			
//			Queue queue = getJMSUtility().getQueueKuecheInput();
//			consumer = getJMSUtility().getSession().createConsumer(queue);
			
		} catch (JMSException e) {
			e.printStackTrace();
		} 
	}
	
	/**
	 * Der Koch wartet auf neue Pizzen in der Queue kueche_input.
	 */
	public void kochen(Message message) {

		String bestellnummer;

		try {
					
			logger.debug("nach receive Pizza aus Kueche_input");
			Pizza pizza = (Pizza)((ObjectMessage) message).getObject();

			bestellnummer = pizza.getBestellnummer();

			logger.debug("KochJMS ("+name+"): Pizza erhalten: "+pizza.getId()+"("+pizza.getBestellnummer()+")");
			
			/* Die Pizzeria soll auch den Status "in zubereitung" anzeigen */
			pizza.setStatus(PizzaStatusEnum.ZUBEREITUNG);
			pizza.setKoch(this.name);

			updateGui(pizza);
			
			if(waitTime){
				Thread.sleep(pizza.getType().getTime() * 1000);
			}
			pizza.setStatus(PizzaStatusEnum.FERTIG);
			updateGui(pizza);
			
			logger.debug("Pizza ("+pizza.getId()+") ist jetzt fertig.");

			/* Die Pizza wird in die Queue kueche_output geschrieben */
			Session session = getJMSUtility().getConnection().createSession(false, Session.AUTO_ACKNOWLEDGE);
			
			MessageProducer producer1 = session.createProducer(getJMSUtility().getQueueKuecheOutput());
			message = session.createObjectMessage(pizza);
			message.setStringProperty("ID", pizza.getId());
			
			producer1.send(message);				
			producer1.close(); // Wird nicht mehr gebraucht.			
			//session.commit();
			session.close();
			
			/* Bestellnummer lesen */
			Session sess2 = getJMSUtility().getConnection().createSession(true, Session.SESSION_TRANSACTED);
			String selector = "ID = '"+bestellnummer+"'";
			MessageConsumer consumer1 = sess2.createConsumer(getJMSUtility().getQueueBestellungOffen(), selector);
			
			message = consumer1.receive();
			Bestellung bestellung = (Bestellung)((ObjectMessage) message).getObject();
			logger.debug("Bestellung konnte gelesen werden ("+bestellung.getBestellungId()+")");

			bestellung.pizzaFertig(pizza);
			consumer1.close(); 
			
			if (bestellung.fertig()) {
				logger.debug("Bestellung ist erledigt und wird in BestellungFertig geschrieben.");
				
				System.out.println("BestellungFertig: "+bestellung.getBestellungId()+" : "+bestellung.getClass().getSimpleName());
				Queue queue = getJMSUtility().getQueueBestellungFertig();
				System.out.println("schreibe Bestellung in Queue: "+queue);
				MessageProducer producer2 = sess2.createProducer(queue);
				message = sess2.createObjectMessage(bestellung);
				message.setStringProperty("Class", bestellung.getClass().getSimpleName());
				producer2.send(message);
				producer2.close();
				
			}else{
				logger.debug("Bestellung weiterhin offen und wird in BestellungOffen geschrieben.");
				
				MessageProducer producer3 = sess2.createProducer(getJMSUtility().getQueueBestellungOffen());
				message = sess2.createObjectMessage(bestellung);
				message.setStringProperty("ID", bestellung.getBestellungId());
				
				producer3.send(message);				
				producer3.close(); // Wird nicht mehr gebraucht.
			}
			
			updateGui(sess2, bestellung);
			
			sess2.commit();
			sess2.close();
			
			System.out.println("Vorgang abgeschlossen ...");
			
		} catch (JMSException e) {
			logger.debug("Fehler beim verarbeiten der Queue Kuech_input.");
			e.printStackTrace();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Über das Topic "Gui_update" werden Statusänderungen kommuniziert.
	 */

	public void updateGui(Session session, Serializable object){
		
		logger.debug("Updating Gui.");
		
		try {
			Topic topic = getJMSUtility().getTopicGuiUpdate();

			Message msg = session.createObjectMessage(object);
			msg.setStringProperty("Sender", this.name);
			MessageProducer producer = session.createProducer(topic);
			
			producer.send(msg);
			
			producer.close();
			
		} catch (JMSException e) {
			
			logger.debug("GUI Update failed.");
			
			e.printStackTrace();
		}	
	}	
	
	public void updateGui(Serializable object){
		try {
			logger.debug("Updating Gui.");
			Session session = getJMSUtility().getConnection().createSession(false, Session.AUTO_ACKNOWLEDGE);
				
			Topic topic = getJMSUtility().getTopicGuiUpdate();

			Message msg = session.createObjectMessage(object);
			msg.setStringProperty("Sender", this.name);
			MessageProducer producer = session.createProducer(topic);
			
			producer.send(msg);
			
			producer.close();
			session.close(); 
			
		} catch (JMSException e) {
			
			logger.debug("GUI Update failed.");
			
			e.printStackTrace();
		}	
	}	
	
	public JMSUtility getJMSUtility(){
		if (jmsUtility == null){
			jmsUtility = new JMSUtility(filiale);
		}
		return jmsUtility;
	}
}
